/*
 * ReactionUtils.cpp
 *
 *  Created on: 22 Aug 2011
 *      Author: Allan
 */

#include "ReactionUtils.h"

// Boost includes
#include <boost/foreach.hpp>

// GeoReact includes
#include "../Database.h"
#include "../Kinetics/MineralKinetics/MineralKineticsDatabase.h"
#include "../Reaction.h"
#include "../ReactionSystem.h"
#include "../SpeciesMap.h"

const Reaction AuxiliarCreateReaction(const ReactionInfo& rinfo, const SpeciesMap& speciesMap)
{
	// The reaction equation
	ReactionEquation equation(speciesMap);
	
	// Set the reaction equation 
	equation.Set(rinfo.reactant, rinfo.products, rinfo.stoichiometries);
	
	// The equation constant of the reaction
	EquilibriumConstant K(rinfo.logKi, rinfo.Ti);
	
	// Create the reaction object
	Reaction reaction(equation, K);
	
	// Return the reaction object
	return reaction;
}

const ReactionSystem CreateReactionSystem(const vector<string>& species, const SpeciesMap& speciesMap)
{
	vector<Reaction> reactions;
	
	BOOST_FOREACH(const string& s, species)
		reactions.push_back(CreateReaction(s, speciesMap));
	
	return ReactionSystem(reactions);
}

const Reaction CreateReaction(const string& species, const SpeciesMap& speciesMap)
{
	if(Database::GetInstance().GetSpeciesInfo(species).HasTag("basis"))
		return CreateBasisReaction(species, speciesMap);
	
	else if(Database::GetInstance().GetSpeciesInfo(species).HasTag("mineral"))
		return CreateMineralReaction(species, speciesMap);
	
	else 
		return CreatePlainReaction(species, speciesMap);
}

const Reaction CreatePlainReaction(const string& species, const SpeciesMap& speciesMap)
{
	// Get the information data of the reaction whose reactant is the given species
	const ReactionInfo reactionInfo = Database::GetInstance().GetReactionInfo(species);
	
	return AuxiliarCreateReaction(reactionInfo, speciesMap);
}

const Reaction CreateBasisReaction(const string& species, const SpeciesMap& speciesMap)
{
	ReactionInfo reactionInfo;
	
	reactionInfo.reactant        = species;
	reactionInfo.products        = {species};
	reactionInfo.stoichiometries = {1.0};
	reactionInfo.logKi           = {0.0};  // K = 1
	reactionInfo.Ti              = {25.0}; // T = 25 degC

	return AuxiliarCreateReaction(reactionInfo, speciesMap);
}

const Reaction CreateMineralReaction(const string& species, const SpeciesMap& speciesMap)
{
	// Create the reaction object for the given mineral
	Reaction mineralReaction = CreatePlainReaction(species, speciesMap);
	
	// Look up at the database of mineral kinetics for the given mineral
	optional<MineralKineticsInfo> mineralKineticsInfo = MineralKineticsDatabase::GetInstance().FindMineralKineticsInfo(species);
	
	// If the kinetics information of the minaral was found, create a mineral kinetics intance and set the mineral reaction with it
	if(mineralKineticsInfo)
	{
		// Create the mineral kinetics object of the given mineral
		MineralKinetics mineralKinetics;
		
		// Set the reaction whose the mineral kinetics will be responsible to compute the rates of reaction
		mineralKinetics.SetReaction(mineralReaction);
		
		// Set the mineral mechanisms of the reaction
		mineralKinetics.SetMechanisms(mineralKineticsInfo->mechanisms, speciesMap);

		// Set the specific surface area of the mineral
		mineralKinetics.SetSpecificSurfaceArea(mineralKineticsInfo->specificSurfaceArea);
		
		// Set the kinetics of the mineral reaction
		mineralReaction.SetKinectics(mineralKinetics);
	}
	
	return mineralReaction;
}

